package com.gimi.cloud.jimi.manage.service.sys.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.gimi.cloud.jimi.common.enums.ResponseCodeEnum;
import com.gimi.cloud.jimi.common.enums.common.RedisKeyEnum;
import com.gimi.cloud.jimi.common.exception.MsgException;
import com.gimi.cloud.jimi.common.jwt.Jwt;
import com.gimi.cloud.jimi.common.utils.*;
import com.gimi.cloud.jimi.commonService.common.JmOperationLogService;
import com.gimi.cloud.jimi.dao.dao.sys.*;
import com.gimi.cloud.jimi.dao.myMapper.core.query.QueryParam;
import com.gimi.cloud.jimi.dao.redis.IRedisService;
import com.gimi.cloud.jimi.dto.base.BaseDTO;
import com.gimi.cloud.jimi.dto.params.*;
import com.gimi.cloud.jimi.dto.sys.SysMasterDTO;
import com.gimi.cloud.jimi.dto.sys.SysMenuDTO;
import com.gimi.cloud.jimi.dto.sys.SysRoleDTO;
import com.gimi.cloud.jimi.entity.sys.SysMasterEntity;
import com.gimi.cloud.jimi.manage.service.sys.MasterService;
import com.gimi.cloud.jimi.manage.service.sys.MenuService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;


import java.util.*;

/**
 * Created by shazhiwei on 2017/12/14.
 */
@Service
public class MasterServiceImpl implements MasterService {
    private final SysMasterDao masterDao;
    private final SysConfigDao configDao;
    private final SysMenuDao menuDao;
    private final SysRoleDao roleDao;
    private final IRedisService redisService;
    private final SysMasterRoleDao masterRoleDao;
    private final JmOperationLogService jmOperationLogService;
    private final MenuService sysMenuService;
    private static Logger logger = LoggerFactory.getLogger(MasterServiceImpl.class);

    @Autowired
    public MasterServiceImpl(SysMasterDao masterDao, IRedisService redisService,
                             SysConfigDao configDao, SysMenuDao menuDao,
                             SysRoleDao roleDao, SysMasterRoleDao masterRoleDao,
                             JmOperationLogService jmOperationLogService,MenuService sysMenuService) {
        this.masterDao = masterDao;
        this.redisService = redisService;
        this.configDao = configDao;
        this.menuDao = menuDao;
        this.roleDao = roleDao;
        this.masterRoleDao = masterRoleDao;
        this.jmOperationLogService = jmOperationLogService;
        this.sysMenuService = sysMenuService;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public AddMasterParams addMaster(AddMasterParams dto) throws Exception {
        //校验用户名是否重复
        if (!checkMasterName(dto.getUsername())) {
            throw new MsgException(ResponseCodeEnum.MANAGE_MASTER_NAME_EXIST);
        }

        SysMasterEntity entity = new SysMasterEntity();
        BeanUtils.copyProperties(dto, entity);
        entity.setPassword(MD5.toMD5(dto.getPassword()));
        masterDao.insertSelective(entity);

        dto.setMaId(entity.getMaId());
        saveBatch(dto);
        dto.setResponseCode(ResponseCodeEnum.SUCCESSED);

        //记录操作日志
        jmOperationLogService.insertAddLog(dto, dto.getMaId() + "", "addMaster", "sys_master", "添加后台操作人员");
        return dto;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})
    public AddMasterParams editMaster(AddMasterParams dto) {
        //更新master对象
        SysMasterEntity entity = new SysMasterEntity();
        BeanUtils.copyProperties(dto, entity);
        masterDao.updateByPrimaryKey(entity);
        //更新关系表
        Integer maId = dto.getMaId();
        String roleIds = dto.getRoleIds();
        //删除maId的关系表记录
        masterRoleDao.deleteByMaId(maId);
        //重新添加关系表记录
        saveBatch(dto);
        redisService.set(RedisKeyEnum.JIMI_MASTER_PRE.getKey() + dto.getUsername(), JsonUtil.toJson(dto));
        dto.setResponseCode(ResponseCodeEnum.SUCCESSED);
        //记录日志
        jmOperationLogService.insertUpdateLog(dto, "editMaster", "sys_master", dto.getMaId() + "", "更新后台操作人员");
        return dto;
    }

    @Override
    public boolean deleteMaster(DeleteMasterParams params) {
        Integer num = masterDao.deleteMaster(params.getMasterId());
        if (num != null && num > 0) {
            //redisService.del(RedisKeyEnum.JIMI_MASTER_PRE.getKey());//需要提供username来删除redis
            jmOperationLogService.insertDeleteLog(params.getMasterId(), "deleteMaster", "sys_master", params.getMasterId() + "", "删除后台操作人员");
            return true;
        }
        return false;
    }

    @Override
    public SysMasterDTO getMaster(LoginParams loginVo) {
        logger.info("===>开始校验用户是否合法");
        //从redis获取对象
        SysMasterDTO masterDTO = getMasterFromRedis(loginVo);

        if (masterDTO != null) {
            masterDTO = checkRedisData(masterDTO);
        }

        //从mysql获取对象
        if (masterDTO == null) {
            masterDTO = getMasterFromDataBase(loginVo);
        }

        if (masterDTO == null) {
            masterDTO = new SysMasterDTO();
            masterDTO.setResponseCode(ResponseCodeEnum.NO_ACCOUNT_TO_GET_TOKEN);
            logger.info(String.format("===>userName=%s的用户不存在", loginVo.getUserName()));
            return masterDTO;
        }

        if (!checkPassword(masterDTO, loginVo)) {
            masterDTO.setResponseCode(ResponseCodeEnum.ERR_PASSWORD_TO_GET_TOKEN);
            logger.info("===>密码错误");
            return masterDTO;
        }

        return masterDTO;
    }


    @Override
    public List<SysMenuDTO> getMasterMenuList(SysMasterDTO masterDTO) {
        List<SysMenuDTO> allList = menuDao.getAllMenuList();
        if (allList == null || allList.size() < 1) {
            throw new MsgException(ResponseCodeEnum.INVALID, "数据异常,没有可用的菜单");
        }
        //如果不是超级管理员则过滤权限
        if (masterDTO.getMaIsadmin() == null || masterDTO.getMaIsadmin() != 1) {
            List<SysMenuDTO> authList = menuDao.getUserMenuList(masterDTO.getMaId());
            List<SysMenuDTO> parent = new ArrayList<>();
            parent = getParent(allList, authList, parent);
            List<SysMenuDTO> list = new ArrayList<>();
            list.addAll(authList);
            list.addAll(parent);
            //过滤重复数据
            HashSet sortSet = new HashSet(list);
            list.clear();
            list.addAll(sortSet);
            for (int i = 0; i < list.size(); i++) {
                for (int j = list.size() - 1; j > i; j--) {
                    if (list.get(i).getMeId().equals(list.get(j).getMeId())) {
                        list.remove(list.get(i));
                    }
                }
            }
            return sortMenu(list);
        }
        return sortMenu(allList);
    }

    @Override
    public List<SysMasterDTO> getMasterList(MasterListParams params) {
        QueryParam param = new QueryParam();
        String sql = "SELECT * FROM sys_master WHERE is_delete=0 AND username= :name OR ma_name = :name LIMIT :index,:pageSize";
        params.setIndex((params.getIndex() - 1) * params.getPageSize());
        param.setParamObject(params);
        param.setSql(sql);
        List<SysMasterDTO> result = masterDao.queryPage(param);
        result = getResponse(result);
        return result;
    }

    @Override
    public SysMasterDTO getMasterSingle(MasterSingleParams params) {
        //查询操作员
        SysMasterDTO result = masterDao.findById(NumberUtil.getInt(params.getMasterId()));
        if (result != null) {
            //查询操作员角色
            List<SysRoleDTO> roleDTOList = roleDao.getRoleListByMasterId(result.getMaId());
            //result.setRoleList(roleDTOList);
            result.setRoleIds(getRoleIdsByList(roleDTOList));
        }
        //校验数据是否合法
        if (result == null) {
            throw new MsgException(ResponseCodeEnum.ERR_USER_INVALID);
        } else if (StringUtil.isBlank(result.getRoleIds())) {
            throw new MsgException(ResponseCodeEnum.ERR_ROLE_LIST_NULL);
        }

        //记录操作日志
        jmOperationLogService.insertQueryLog(new BaseDTO(), "masterSingle", "sys_master", params.getMasterId(), "查询要修改的数据作回显");
        return result;
    }

    @Override
    public Long getCount(MasterListParams params) {
        QueryParam param = new QueryParam();
        String sql = "SELECT count(1) FROM sys_master WHERE is_delete=0 AND username= :name OR ma_name = :name";
        param.setParamObject(params);
        param.setSql(sql);
        return masterDao.getCount(param);
    }

    @Override
    public Map<String, Object> getMasterPermissions(SysMasterDTO sysMasterDTO, Integer masterId) {
        Map<String, Object> masterPermissions = new HashMap<>();
        SysMenuDTO sysMenuDTO = new SysMenuDTO();
        MyBeanUtils.copyPropertiesIgnoreNull(sysMasterDTO, sysMenuDTO);
        List<SysMenuDTO> menus = sysMenuService.getMenuByMasterId(sysMenuDTO, masterId);
        //用户的菜单权限
        masterPermissions.put("authMenus", sysMenuService.menuToTree(menus));
        Map<String, Object> permissions = sysMenuService.getPermissions(sysMenuDTO, menus);
        //添加权限信息
        masterPermissions.putAll(permissions);
        return masterPermissions;
    }

    @Override
    public String createTokenAndSet2Redis(SysMasterDTO masterDTO) {
        JSONObject masterTokenRule = redisService.getJSONObjectConfigByKey(RedisKeyEnum.JIMI_CONFIG_PRE.getKey() + "manage_token_rule");
        if (null == masterTokenRule) {
            String tokenRule = configDao.getConfigByKey("manage_token_rule");
            if(StringUtil.isBlank(tokenRule)){
                logger.error("生成管理员登录token失败，未获取到登录token配置信息");
                return null;
            }
            masterTokenRule= JSON.parseObject(tokenRule);
            redisService.setObject(RedisKeyEnum.JIMI_CONFIG_PRE.getKey() + "manage_token_rule",masterTokenRule);
        }
        String redisKey = RedisKeyEnum.JIMI_MASTER_TOKEN_PRE.getKey() + masterDTO.getUsername();
        List<String> masterTokens = null;
        //非单一登录限制
        if (!"1".equals(masterTokenRule.getString("is_single_login"))) {
            //获取系统中存在的登录token
            masterTokens = redisService.getList(redisKey, String.class);
        }
        if (null == masterTokens) {
            masterTokens = new ArrayList<>();
        }
        Map<String, Object> validResult;
        //使用HashSet，避免重复生成token
        HashSet<String> masterNewTokens = new HashSet<>();
        for (String tempToken : masterTokens) {
            validResult = Jwt.validToken(tempToken, masterTokenRule.getString("jwt_token_secret"));
            //如果当前状态是有效的token则保留该token
            if (null != validResult && null != validResult.get("state") && "VALID".equals(validResult.get("state").toString())) {
                masterNewTokens.add(tempToken);
            }
        }
        masterTokens.clear();
        //设置当前用户的token为有效的token
        masterTokens.addAll(masterNewTokens);
        Map<String, Object> payload = new HashMap<>(3);
        payload.put("maId", masterDTO.getMaId());
        payload.put("userName", masterDTO.getUsername());
        //过期时间
        payload.put("ext", System.currentTimeMillis() + masterTokenRule.getLong("validate_times"));
        String token = Jwt.createToken(payload, masterTokenRule.getString("jwt_token_secret"));
        masterTokens.add(token);
        redisService.setList(redisKey, masterTokens);
        return token;
    }


    private SysMasterDTO getMasterFromRedis(LoginParams loginVo) {
        SysMasterDTO result = null;
        try {
            String redisKey = RedisKeyEnum.JIMI_MASTER_PRE.getKey() + loginVo.getUserName();//用户名不能重复
            logger.info("===>尝试从redis获取master,key = " + redisKey);
            String redisMaster = redisService.get(redisKey);
            if (!StringUtil.isBlank(redisMaster)) {
                result = JsonUtil.jsonToBean(redisMaster, SysMasterDTO.class);
                logger.info("===>从redis获取数据" + redisMaster);
            }
        } catch (Exception e) {
            logger.error(String.format("===>从redis获取用户%s失败", loginVo.getUserName()));
            e.printStackTrace();
        }
        return result;
    }

    private SysMasterDTO getMasterFromDataBase(LoginParams loginVo) {
        SysMasterDTO result = null;
        try {
            logger.info(String.format("===>尝试从数据库获取master:%s", loginVo.getUserName()));
            SysMasterDTO data = masterDao.findByUserName(loginVo.getUserName());
            if (data != null) {
                result = data;
                String redisKey = RedisKeyEnum.JIMI_MASTER_PRE.getKey() + loginVo.getUserName();//用户名不能重复
                String str = JsonUtil.toJson(result);
                redisService.set(redisKey, JsonUtil.toJson(result));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    private boolean checkPassword(SysMasterDTO dto, LoginParams loginVo) {
        boolean result = false;
        String serverPassword = dto.getPassword();//服务器密码
        String loginPassword = loginVo.getPassword();//用户提交密码
        logger.info("===>服务器密码：" + serverPassword + ",用户提交的密码:" + loginPassword);
        String md5pwd = MD5.toMD5(loginPassword);
        logger.info("===>md5后:" + md5pwd);
        if (md5pwd.equalsIgnoreCase(serverPassword)) {
            result = true;
        }
        return result;
    }

    SysMasterDTO checkRedisData(SysMasterDTO masterDTO) {
        if (StringUtil.isBlank(masterDTO.getUsername())) {
            return null;
        }
        if (StringUtil.isBlank(masterDTO.getPassword())) {
            return null;
        }
        return masterDTO;
    }

    public List<SysMenuDTO> sortMenu(List<SysMenuDTO> rootMenu) {
        List<SysMenuDTO> menuList = new ArrayList<>();
        for (int i = 0; i < rootMenu.size(); i++) {
            Integer pId = rootMenu.get(i).getMePid();
            if (pId != null && pId == 0) {
                menuList.add(rootMenu.get(i));
            }
        }
        for (SysMenuDTO menu : menuList) {
            menu.setChilds(getChild(menu.getMeId(), rootMenu));
        }
        return menuList;
    }

    private List<SysMenuDTO> getChild(Integer id, List<SysMenuDTO> rootMenu) {
        // 子菜单
        List<SysMenuDTO> childList = new ArrayList<>();
        for (SysMenuDTO menu : rootMenu) {
            // 遍历所有节点，将父菜单id与传过来的id比较
            if (menu.getMePid() != null) {
                if (menu.getMePid().equals(id)) {
                    childList.add(menu);
                }
            }
        }
        // 把子菜单的子菜单再循环一遍
        for (SysMenuDTO menu : childList) {
            if (StringUtils.isBlank(menu.getMeLink())) {
                menu.setChilds(getChild(menu.getMeId(), rootMenu));// 递归
            }
        }
        if (childList.size() == 0) {
            // 递归出口
            return null;
        }
        return childList;
    }

    /**
     * 美化数据
     *
     * @return
     */
    private List<SysMasterDTO> getResponse(List<SysMasterDTO> list) {
        List<SysMasterDTO> result = null;
        if (list != null && list.size() > 0) {
            result = new ArrayList<>();
            for (SysMasterDTO dto : list) {
                SysMasterDTO tmp = new SysMasterDTO();
                tmp.setMaId(dto.getMaId());
                tmp.setUsername(dto.getUsername());
                tmp.setMaName(dto.getMaName());
                tmp.setMaAvatar(dto.getMaAvatar());
                tmp.setMaSexStr(StateUtil.changeSex(dto.getMaSex()));
                tmp.setMaTel(dto.getMaTel());
                tmp.setIsAdminStr(StateUtil.changeYesOrNo(dto.getMaIsadmin()));
                tmp.setStateStr(StateUtil.changeMasterState(dto.getMaState()));
                result.add(tmp);
            }
        }
        return result;
    }

    /**
     * 校验用户名是否重复
     */
    private boolean checkMasterName(String userName) {
        boolean result = false;
        SysMasterDTO data = masterDao.findByUserName(userName);
        if (data == null) {
            result = true;
        }
        return result;
    }

    private void saveBatch(AddMasterParams dto) {
        String sql = "INSERT INTO sys_master_role(ma_id,ro_id,mr_created,add_uid,add_uname,add_time,update_uid,update_uname,update_time) VALUES ";
        String values = getValues(dto);
        if (!StringUtil.isBlank(values)) {
            sql = sql + values;
            masterRoleDao.saveBatch(sql);
        } else {
            throw new MsgException(ResponseCodeEnum.ERR_ROLE_LIST_NULL);
        }
    }

    /**
     * 拼凑batchSave values
     */
    private static String getValues(AddMasterParams dto) {
        Integer maId = dto.getMaId();
        String roleIds = dto.getRoleIds();
        String[] roIds = roleIds.split(",");
        StringBuilder sb = new StringBuilder();
        if (roIds != null && roIds.length > 0) {
            for (int i = 0; i < roIds.length; i++) {
                sb.append("(");
                sb.append(maId).append(",").append(roIds[i]).append(",").append("now()")
                        .append(",").append(dto.getAddUid()).append(",'").append(dto.getAddUname()).append("',").append("now()")
                        .append(",").append(dto.getUpdateUid()).append(",'").append(dto.getUpdateUname()).append("',").append("now()");
                sb.append("),");
            }
            if (sb.length() > 0) {
                sb.delete(sb.length() - 1, sb.length());
            }
        }
        return sb.toString();
    }

    private String getRoleIdsByList(List<SysRoleDTO> roleDTOList) {
        StringBuilder result = new StringBuilder();
        if (roleDTOList != null && roleDTOList.size() > 0) {
            for (SysRoleDTO dto : roleDTOList) {
                result.append(dto.getRoId()).append(",");
            }
            if (result.length() > 0) {
                result.delete(result.length() - 1, result.length());
            }
        }
        return result.toString();
    }

    private List<SysMenuDTO> getParent(List<SysMenuDTO> all, List<SysMenuDTO> auth, List<SysMenuDTO> parent) {
        if (auth != null && auth.size() > 0) {
            for (SysMenuDTO dto : auth) {
                if (dto.getMePid() != null && dto.getMePid() != 0) {
                    SysMenuDTO par = getByMeId(all, dto.getMePid());
                    if (par != null) {
                        parent.add(par);
                        //auth.remove(dto);
                        if (par.getMePid() != null && par.getMePid() != 0) {
                            auth.add(par);
                            getParent(all, auth, parent);
                        }
                    }
                }
            }
        }
        return parent;
    }

    private SysMenuDTO getByMeId(List<SysMenuDTO> all, Integer meId) {
        SysMenuDTO result = null;
        if (all != null && all.size() > 0) {
            for (SysMenuDTO dto : all) {
                System.out.println(dto.getMeId());
                if (dto.getMeId().equals(meId)) {
                    result = dto;
                    break;
                }
            }
        }
        return result;
    }

    @Override
    public SysMasterDTO load(SysMasterDTO dto, Integer id)   {
        SysMasterEntity masterEntity = masterDao.queryBy("ma_id",id);
        if(null == masterEntity){
            return null;
        }
        SysMasterDTO masterDTO = new SysMasterDTO();
        MyBeanUtils.copyPropertiesIgnoreNull(masterEntity,masterDTO);
        return masterDTO;
    }

}
